#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/atomic.h>
#include <linux/delay.h>

static struct cdev read_write_concurrent_cdev;
static struct class* read_write_concurrent_cls;
static struct device* read_write_concurrent_device;
static dev_t read_write_concurrent_devno;

static const char* class_name = "read_write_concurrent";
static const char* module_name = "read_write_concurrent";
static const char* device_name = "read_write_concurrent";

static int read_write_concurrent_open(struct inode* inode, struct file* filp)
{
    return 0;
}

static int read_write_concurrent_release(struct inode* inode, struct file* filp)
{
    return 0;
}


static ssize_t read_write_concurrent_read(struct file* filp, char __user* buffer, size_t count, loff_t* f_pos)
{
    printk("read enter, f_pos = %px, *f_pos = %lld\n", f_pos, *f_pos);
    *f_pos = 1111;
    msleep_interruptible(1000);
    printk("read exit, f_pos = %px, *f_pos = %lld\n", f_pos, *f_pos);

    return 0;
}

static ssize_t read_write_concurrent_write(struct file* filp, const char __user* buf, size_t size, loff_t* ppos)
{
    printk("write enter, ppos = %px, *ppos = %lld\n", ppos, *ppos);
    *ppos = 2222;
    msleep_interruptible(1000);
    printk("write exit, ppos = %px, *ppos = %lld\n", ppos, *ppos);

    return size;
}


static const struct file_operations read_write_concurrent_fops =
{
    .owner = THIS_MODULE,
    .write = read_write_concurrent_write,
    .read = read_write_concurrent_read,
    .open = read_write_concurrent_open,
    .release = read_write_concurrent_release,
};

static int __init read_write_concurrent_init(void)
{
    int ret;
    ret = alloc_chrdev_region(&read_write_concurrent_devno, 0, 1, module_name);
    if (0 != ret)
    {
        return ret;
    }

    cdev_init(&read_write_concurrent_cdev, &read_write_concurrent_fops);
    read_write_concurrent_cdev.owner = THIS_MODULE;
    ret = cdev_add(&read_write_concurrent_cdev, read_write_concurrent_devno, 1);
    if (ret != 0)
    {
        goto fail1;
    }

    read_write_concurrent_cls = class_create(THIS_MODULE, class_name);
    if (IS_ERR(read_write_concurrent_cls))
    {
        ret = PTR_ERR(read_write_concurrent_cls);
        goto fail2;
    }

    read_write_concurrent_device = device_create(read_write_concurrent_cls, NULL, read_write_concurrent_devno, NULL, device_name);
    if (IS_ERR(read_write_concurrent_device))
    {
        ret = PTR_ERR(read_write_concurrent_device);
        goto fail3;
    }
    return 0;

fail3:
    class_destroy(read_write_concurrent_cls);
fail2:
    cdev_del(&read_write_concurrent_cdev);
fail1:
    unregister_chrdev_region(read_write_concurrent_devno, 1);
    return ret;
}
module_init(read_write_concurrent_init);

static void __exit read_write_concurrent_exit(void)
{
    device_destroy(read_write_concurrent_cls, read_write_concurrent_devno);
    class_destroy(read_write_concurrent_cls);
    cdev_del(&read_write_concurrent_cdev);
    unregister_chrdev_region(read_write_concurrent_devno, 1);
}
module_exit(read_write_concurrent_exit);

MODULE_LICENSE("GPL");
